﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// enumerate IList
// traverse using Stack<T>; null out frames for async traversal
// ancestory non-object traversal; HasAncestor()

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Collections;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Text;
using System.Dynamic;
using System.Xml.Linq;

using King.Extensions;
using King.Reflection;
using King.Text;
using King.Traverse;

namespace King.Declarative {

    [Declaration(AdaptorType = typeof(Adaptor))]
    public class GenericDeclaration : IEnumerable {
        
        public static bool IsSubstantial(object value) {
            if (value == null)
                return false;

            var declaration = value as GenericDeclaration;
            if (declaration != null)
                return declaration.IsSubstantialCached;

            return Evaluation<SubstanceInfo>.Evaluate(value).Any();
        }

        protected class Adaptor : DeclarationAdaptor {
            
            public class SuppressSubstitute : Adaptor {
                public SuppressSubstitute(Type adaptedType)
                    : base(adaptedType) { }

                public override Value Substitute(DeclarationContext frame, Value target) {
                    return target;
                }
            }

            public Adaptor(Type adaptedType)
                : base(adaptedType) {
            }

            public override int GetFlags(object target) {
                var declaration = (GenericDeclaration)target;
                return declaration.m_flags;
            }
            public override void SetFlags(object target, int state) {
                var declaration = (GenericDeclaration)target;

                while (true) {
                    var oldFlags = declaration.m_flags;
                    var newFlags = oldFlags | state;

                    if (oldFlags == Interlocked.CompareExchange(
                        ref declaration.m_flags, newFlags, oldFlags))
                        break;
                }
            }

            public override Value GetLeaf(DeclarationContext frame, object target) {
                var declaration = (GenericDeclaration)target;
                var children = declaration.GetLeaf(frame);
                return new Value(children);
            }
            public override Value Children(DeclarationContext frame) {
                var declaration = (GenericDeclaration)frame.Object;
                var children = declaration.Children(frame);
                return new Value(children);
            }
            public override Value Substance(object target) {
                var declaration = (GenericDeclaration)target;
                return new Value(declaration.Substance());
            }
            public override Value Substitute(DeclarationContext frame, Value value) {
                var declaration = (GenericDeclaration)frame.Object;
                
                var result = declaration.Substitute(frame, value);
                if (result == null)
                    result = declaration.Substitute(frame, value.Object);

                return new Value(result);
            }
            public override void InitializeFrame(DeclarationContext frame) {
                var declaration = (GenericDeclaration)frame.Object;
                declaration.InitializeFrame(frame);
            }
            public override void DeinitializeFrame(DeclarationContext frame) {
                var declaration = (GenericDeclaration)frame.Object;
                declaration.DeinitializeFrame(frame);
            }
        }

        public static readonly DeclarationFlush Flush = new DeclarationFlush();

        protected internal static readonly IEnumerable Substantial = new SubstantialDeclaration();
        protected internal static readonly IEnumerable Vanish = new object[0];

        private int m_flags;
        private List<object> m_declarations;

        protected internal bool IsSubstantialCached {
            get {
                var info = DeclarationLoader.GetInfo(GetType());
                var isSubstantial = info.IsSubstantial(this);
                if (isSubstantial != null)
                    return (bool)isSubstantial;

                return Evaluation<SubstanceInfo>.Evaluate(this).Any();
            }
        }
        protected IEnumerable<Value> Evaluate() {
            return Evaluation<EvaluateInfo>.Evaluate(this);
        }
        protected IEnumerable<object> Children() {
            return m_declarations;
        }

        protected virtual object GetLeaf(DeclarationContext frame) {
            return null;
        }
        protected virtual IEnumerable Children(DeclarationContext frame) {
            return m_declarations;
        }
        protected virtual IEnumerable Substitute(DeclarationContext frame, Value target) {
            return null;
        }
        protected virtual IEnumerable Substitute(DeclarationContext frame, object target) {
            return null;
        }
        protected virtual IEnumerable Substance() {
            return m_declarations;
        }

        protected virtual void InitializeFrame(DeclarationContext frame) {
        }
        protected virtual void DeinitializeFrame(DeclarationContext frame) {
        }

        public virtual void Add(object o) {
            if (m_declarations == null)
                m_declarations = new List<object>();

            m_declarations.Add(o);
        }
        public virtual IEnumerator GetEnumerator() {
            return Evaluate().GetEnumerator();
        }
    }
    public class Declaration : GenericDeclaration, IEnumerable<object> {
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
        public new virtual IEnumerator<object> GetEnumerator() {
            return Evaluate().Select(o => o.Object).GetEnumerator();
        }
    }
    public sealed class DeclarationFlush {
        internal DeclarationFlush() { }
    }
    public sealed class SubstantialDeclaration : Declaration {

        private static readonly IEnumerable ArbitrarySubstance = new[] { new object() };

        protected override IEnumerable Substance() {
            return ArbitrarySubstance;
        }
    }

    [Declaration(IsLeaf = true)]
    public class Leaf : Declaration {

        public static readonly Leaf Singleton = new Leaf();
        public static readonly Leaf VanishingSingleton = new Leaf(Vanish);

        private object m_value;

        public Leaf(object value = null) {
            m_value = value;
        }

        protected override object GetLeaf(DeclarationContext frame) {
            return m_value;
        }

        public object Value {
            get { return m_value; }
        }

        public override void Add(object o) {
            throw new NotSupportedException();
        }
    }

    public abstract class DeclarationSubstitutionContinuation : Declaration {

        private DeclarationContext m_frame;
        private bool m_continueAfter;

        internal DeclarationSubstitutionContinuation(DeclarationContext frame, bool continueAfter = false) {
            m_frame = frame;
            m_continueAfter = continueAfter;
        }

        public DeclarationContext Frame {
            get { return m_frame; }
        }
        public bool ContinueSubstitutionAfter {
            get { return m_continueAfter; }
        }

        protected override void InitializeFrame(DeclarationContext frame) {
            if (m_frame.IsNull)
                return;

            if (m_continueAfter)
                frame.ContinueSubstitutionAfter(m_frame);
            else
                frame.ContinueSubstitutionAt(m_frame);
        }
    }
    public class DeclarationContinueSubstitutionAt : DeclarationSubstitutionContinuation {
        public DeclarationContinueSubstitutionAt(DeclarationContext frame)
            : base(frame, false) { }
    }
    public class DeclarationContinueSubstitutionAfter : DeclarationSubstitutionContinuation {
        public DeclarationContinueSubstitutionAfter(DeclarationContext frame)
            : base(frame, true) { }
    }

    public class Flatten : GenericDeclaration, IEnumerable<object> {
        public new IEnumerator<object> GetEnumerator() {
            var evaluation = Evaluation<FlattenInfo>.Evaluate(Children());
            var enumeration = evaluation.Select(o => o.Object);
            return enumeration.GetEnumerator();
        }
    }
    public class Flatten<T> : GenericDeclaration, IEnumerable<T> {
        public new IEnumerator<T> GetEnumerator() {
            foreach (var o in new Flatten { Children() })
                yield return (T)o;
        }
    }
}

namespace King.Declarative {

    public class DeclarationAttribute : Attribute {
        public const string AllUnknownTypes = "AllUnknownTypes";
        public const string AllAdaptedLeafTypes = "AllAdaptedLeafTypes";
        public const string AllTypes = "AllTypes";
        public const string AllCharacters = "AllCharacters";

        public DeclarationAttribute() {
        }

        public object SubstituteTypes { get; set; }
        public object SubstituteCharacters { get; set; }
        public bool UseDedicatedStack { get; set; }
        public bool UsePhysicalStack { get; set; }
        public bool IsLeaf { get; set; }
        public Type AdaptorType { get; set; }
    }
    public abstract class DeclarationAdaptor {

        private Type m_adaptedType;

        internal DeclarationAdaptor(Type adaptedType) {
            m_adaptedType = adaptedType;
        }

        public Type AdaptedType {
            get { return m_adaptedType; }
        }

        public virtual int GetFlags(object target) {
            return default(int);
        }
        public virtual void SetFlags(object target, int state) {
        }

        public virtual void InitializeFrame(DeclarationContext frame) {
        }
        public virtual void DeinitializeFrame(DeclarationContext frame) {
        }

        public virtual object Prolog(DeclarationContext frame, object value) {
            return null;
        }
        public virtual Value Substitute(DeclarationContext frame, Value value) {
            return default(Value);
        }

        public virtual Value GetLeaf(DeclarationContext frame, object target) {
            return default(Value);
        }

        public abstract Value Substance(object target);
        public abstract Value Children(DeclarationContext frame);
    }

    [Flags]
    internal enum DeclarationFlag {
        IsSubstantial = 1 << 0,
        IsVacuous = 1 << 1,
    }

    internal static partial class DeclarationLoader {

        private static object LoaderLock = new object();

        static DeclarationLoader() {
            s_unknownInfo = new UnknownInfo();
            s_enumerableInfo = new TransparentInfo();

            s_infos = new Dictionary<Type, DeclarationInfo>();
            s_infos[typeof(DeclarationFlush)] = new FlushInfo();
            
            s_string = new StringInfo();
            s_character = new ChacaterInfo();

            s_knownSubstituteTargets = new HashSet<Type>();
            s_infosThatSubstitute = new List<DeclarationInfo>();
            s_hasDedicatedStack = new Dictionary<Type, bool>();
        }

        private static Dictionary<Type, DeclarationInfo> s_infos;
        private static List<DeclarationInfo> s_infosThatSubstitute;
        private static Dictionary<Type, bool> s_hasDedicatedStack;
        private static DeclarationInfo s_string;
        private static DeclarationInfo s_character;
        private static DeclarationInfo s_unknownInfo;
        private static DeclarationInfo s_enumerableInfo;
        private static HashSet<Type> s_knownSubstituteTargets;

        [DebuggerDisplay(@"\{Type = {m_type} Adaptor = {m_adaptorType}}")]
        private sealed class AdaptedDeclarationInfo : DeclarationInfo {

            private static Value GetReturnValue(Value value) {

                object obj = null;

                while (true) {

                    if (value.IsReference)
                        return value;

                    obj = value.Object;

                    var array = obj as Array;
                    if (array == null || array.Length != 1)
                        break;

                    // unwrap singleton arrays
                    var objectArray = array as object[];
                    if (objectArray != null) {
                        obj = objectArray[0];
                        continue;
                    }

                    // unwrap singleton char arrays
                    var characterArray = array as char[];
                    if (characterArray != null)
                        return new Value(characterArray[0]);

                    // unwrap singleton char? arrays
                    var maybeCharacterArray = array as char?[];
                    if (characterArray != null)
                        return new Value(maybeCharacterArray[0]);
                }

                return new Value(obj);
            }

            private static readonly Type[] AllUnknownTypes = new Type[] { };
            private static readonly Type[] AllAdaptedLeafTypes = new Type[] { };
            private static readonly Type[] AllTypes = new Type[] { };
            private static readonly char[] AllCharacters = new char[] { };

            private readonly Type m_type;
            private readonly DeclarationInfo m_baseInfo;
            private readonly Type m_adaptorType;
            private readonly DeclarationAdaptor m_adaptor;
            private readonly bool m_overridesToString;
            private readonly bool m_overridesSubstance;

            private readonly bool m_hasDedicatedStack;
            private readonly Type[] m_auxiliaryStacks;
            private readonly bool m_usePhysicalStack;

            private readonly Type[] m_substitutesTypes;
            private readonly HashSet<Type> m_substituteTargetSet;
            private readonly char[] m_substituteCharacters;
            private readonly bool m_alwaysHasSubstance;
            private readonly bool m_isLeaf;

            internal AdaptedDeclarationInfo(Type type) {
                m_type = type;
                m_substituteTargetSet = new HashSet<Type>();
                m_auxiliaryStacks = new Type[] { };

                var baseInfo = GetInfo(type.BaseType) as AdaptedDeclarationInfo;
                if (baseInfo != null) {
                    m_baseInfo = baseInfo;
                    m_adaptorType = baseInfo.m_adaptorType;

                    // substitute
                    m_substitutesTypes = baseInfo.m_substitutesTypes;
                    m_substituteCharacters = baseInfo.m_substituteCharacters;
                    m_alwaysHasSubstance = baseInfo.m_alwaysHasSubstance;
                    m_isLeaf = baseInfo.m_isLeaf;
                    m_usePhysicalStack = baseInfo.m_usePhysicalStack;
                    m_auxiliaryStacks = baseInfo.m_auxiliaryStacks;

                    // overrides
                    m_overridesToString = baseInfo.m_overridesToString;
                    m_overridesSubstance = baseInfo.m_overridesSubstance;
                }

                // ToString
                if (!m_overridesToString)
                    m_overridesToString = type.Overrides("ToString", parameterTypes: Type.EmptyTypes);

                if (!m_overridesSubstance)
                    m_overridesSubstance = type.Overrides("Substance");
                m_alwaysHasSubstance = !m_overridesSubstance;

                var attribute = m_type.GetAttribute<DeclarationAttribute>();

                // leaf
                if (attribute.IsLeaf)
                    m_isLeaf = true;

                // adaptor type
                if (attribute.AdaptorType != null)
                    m_adaptorType = attribute.AdaptorType;

                // add type to inherited list of private stacks
                m_hasDedicatedStack = attribute.UseDedicatedStack;
                if (attribute.UseDedicatedStack)
                    m_auxiliaryStacks = new List<Type>(
                        baseInfo.m_auxiliaryStacks.Concat(type)
                    ).ToArray();

                // activate adaptor
                m_adaptor = (DeclarationAdaptor)m_adaptorType.Activate(type);

                // node never skips frames when being substituted (i.e. StartLine)
                if (attribute.UsePhysicalStack)
                    m_usePhysicalStack = true;

                // character substitution
                if (attribute.SubstituteCharacters as string == DeclarationAttribute.AllCharacters)
                    m_substituteCharacters = AllCharacters;
                else if (attribute.SubstituteCharacters != null)
                    m_substituteCharacters = attribute.SubstituteCharacters.ToEnumerable<char>().ToArray();

                // substitutes
                if (attribute.SubstituteTypes as string == DeclarationAttribute.AllUnknownTypes)
                    m_substitutesTypes = AllUnknownTypes;
                else if (attribute.SubstituteTypes as string == DeclarationAttribute.AllTypes)
                    m_substitutesTypes = AllTypes;
                else if (attribute.SubstituteTypes as string == DeclarationAttribute.AllAdaptedLeafTypes)
                    m_substitutesTypes = AllAdaptedLeafTypes;
                else if (attribute.SubstituteTypes != null)
                    m_substitutesTypes = attribute.SubstituteTypes.ToEnumerable<Type>().ToArray();
            }
            internal override void UpdateSubstitutionTargets(Type type) {
                if (!ImplementesSubstitute)
                    return;

                // special case characters
                if (m_substitutesTypes == null) {
                    Contract.Assert(m_substituteCharacters != null);
                    return;
                }

                var info = DeclarationLoader.GetInfo(type);

                // substitute all types
                if (m_substitutesTypes == AllTypes)
                    m_substituteTargetSet.Add(type);

                // substitute leaf types
                else if (m_substitutesTypes == AllAdaptedLeafTypes) {

                    if (info.HasAdaptor && !info.IsInternalNode)
                        m_substituteTargetSet.Add(type);
                }

                // substitute all unknown types
                else if (m_substitutesTypes == AllUnknownTypes) {

                    if (info.IsUnknown)
                        m_substituteTargetSet.Add(type);
                }

                // substitute specific types
                else {
                    for (int i = 0; i < m_substitutesTypes.Length; i++) {
                        if (m_substitutesTypes[i].IsAssignableFrom(type)) {
                            m_substituteTargetSet.Add(type);
                            break;
                        }
                    }
                }
            }

            private bool IsSubstituteCharacter(char character) {
                if (m_substituteCharacters == null)
                    return false;

                if (m_substituteCharacters == AllCharacters)
                    return true;

                for (int i = 0; i < m_substituteCharacters.Length; i++) {
                    if (m_substituteCharacters[i] == character)
                        return true;
                }

                return false;
            }

            internal override DeclarationAdaptor Adaptor {
                get { return m_adaptor; }
            }
            internal override bool HasAdaptor {
                get { return true; }
            }

            internal override bool IsInternalNode {
                get { return !m_isLeaf; }
            }
            internal override Value GetLeaf(DeclarationContext frame, Value value) {
                return m_adaptor.GetLeaf(frame, value.Object);
            }

            internal override Type[] DedicatedStacks() {
                return m_auxiliaryStacks;
            }
            internal override bool HasDedicatedStack {
                get { return m_hasDedicatedStack; }
            }

            internal override Value Children(DeclarationContext frame) {
                return GetReturnValue(m_adaptor.Children(frame));
            }
            internal override Value Substance(object target) {
                return GetReturnValue(m_adaptor.Substance(target));
            }
            internal override void InitializeFrame(DeclarationContext frame) {
                m_adaptor.InitializeFrame(frame);
            }
            internal override void DeinitializeFrame(DeclarationContext frame) {
                m_adaptor.DeinitializeFrame(frame);
            }

            internal override bool ImplementsToString {
                get { return m_overridesToString; }
            }

            internal override DeclarationFlag GetFlags(object target) {
                return (DeclarationFlag)m_adaptor.GetFlags(target);
            }
            internal override void SetFlags(object target, DeclarationFlag state) {
                m_adaptor.SetFlags(target, (int)state);
            }

            internal override Value Prolog(DeclarationContext frame, Value value) {
                return GetReturnValue(new Value(m_adaptor.Prolog(frame, value.Object)));
            }
            internal override Value Substitute(DeclarationContext frame, Value value) {
                if (value.IsCharacter && !IsSubstituteCharacter((char)value.Character))
                    return default(Value);

                return GetReturnValue(m_adaptor.Substitute(frame, value));
            }
            internal override bool IsSubstituteType(Type type) {
                if (!ImplementesSubstitute)
                    return false;

                // oprimize character case
                if (m_substituteCharacters != null && type == typeof(char))
                    return true;

                // cache if this type is substituted by this info and every other info
                CacheSubstituteTarget(type);

                // check cache of targets
                return m_substituteTargetSet.Contains(type);
            }
            internal override bool UsePhysicalStack {
                get { return m_usePhysicalStack; }
            }

            internal override bool ImplementesSubstitute {
                get { return m_substitutesTypes != null || m_substituteCharacters != null; }
            }
            internal override bool AlwaysHasSubstance {
                get { return m_alwaysHasSubstance; }
            }
        }
        private sealed class FlushInfo : DeclarationInfo {
            internal override DeclarationFlag GetFlags(object target) {
                return DeclarationFlag.IsVacuous;
            }
            internal override bool IsFlush {
                get { return true; }
            }
        }
        private sealed class TransparentInfo : DeclarationInfo {

            internal override Value Children(DeclarationContext frame) {
                // should enumerate for children
                throw new InvalidOperationException();
            }
            internal override Value Substance(object target) {
                // should enumerate for children
                throw new InvalidOperationException();
            }
            internal override bool IsTransparent {
                get { return true; }
            }
            internal override bool IsInternalNode {
                get { return true; }
            }
            internal override bool EnumerateForChildren {
                get { return true; }
            }

            // todo
            internal override DeclarationFlag GetFlags(object target) {
                return default(DeclarationFlag);
            }
            internal override void SetFlags(object target, DeclarationFlag state) {
            }
        }
        private sealed class StringInfo : DeclarationInfo {

            internal override Value Children(DeclarationContext frame) {
                // should enumerate for children
                throw new InvalidOperationException();
            }
            internal override Value Substance(object target) {
                // should enumerate for children
                throw new InvalidOperationException();
            }
            internal override bool IsTransparent {
                get { return false; }
            }
            internal override bool IsInternalNode {
                get { return true; }
            }
            internal override bool EnumerateForChildren {
                get { return true; }
            }
        }
        private sealed class ChacaterInfo : DeclarationInfo {

            internal override bool AlwaysHasSubstance {
                get { return true; }
            }
            internal override bool ImplementsToString {
                get { return true; }
            }
        }
        private sealed class UnknownInfo : DeclarationInfo {

            internal override bool AlwaysHasSubstance {
                get { return true; }
            }
            internal override bool ImplementsToString {
                get { return true; }
            }
            internal override bool IsUnknown {
                get { return true; }
            }
        }

        internal static void CacheSubstituteTarget(Type type) {
            lock (LoaderLock) {
                if (s_knownSubstituteTargets.Contains(type))
                    return;

                foreach (var info in s_infosThatSubstitute)
                    info.UpdateSubstitutionTargets(type);

                s_knownSubstituteTargets.Add(type);
            }
        }
        internal static bool HasDedicatedStack(Type type) {
            lock (LoaderLock) {
                
                // hit cache
                bool result;
                if (s_hasDedicatedStack.TryGetValue(type, out result))
                    return result;
                
                // load info (which populates cache)
                GetInfo(type);

                // hit cache
                return HasDedicatedStack(type);
            }
        }
        internal static DeclarationInfo GetInfo(Type type) {

            lock (LoaderLock) {

                // special case char, string, and all arrays
                if (type == typeof(char))
                    return s_character;
                if (type == typeof(string))
                    return s_string;
                if (type.IsArray)
                    return s_enumerableInfo;

                // check cache
                var info = s_infos.GetValueOrDefault(type);
                if (info != null)
                    return info;

                // adapted type
                if (type.IsDefined<DeclarationAttribute>())
                    info = new AdaptedDeclarationInfo(type);

                // transparent type
                else if (typeof(IEnumerable).IsAssignableFrom(type))
                    info = s_enumerableInfo;

                // unknown type
                else
                    info = s_unknownInfo;

                // update substitution caches
                if (info.ImplementesSubstitute) {
                    s_infosThatSubstitute.Add(info);
                    foreach (var value in s_knownSubstituteTargets)
                        info.UpdateSubstitutionTargets(value);
                }

                // cache which infos have dedicated stacks
                s_hasDedicatedStack[type] = info.HasDedicatedStack;

                // cache info
                return s_infos[type] = info;
            }
        }
    }
    internal abstract class DeclarationInfo {

        // identity
        internal virtual bool HasAdaptor {
            get { return false; }
        }
        internal virtual DeclarationAdaptor Adaptor {
            get { return null; }
        }

        // initialization
        internal virtual void InitializeFrame(DeclarationContext frame) {
        }
        internal virtual void DeinitializeFrame(DeclarationContext frame) {
        }

        // flags (immutable after setting)
        internal virtual DeclarationFlag GetFlags(object target) {
            return default(DeclarationFlag);
        }
        internal virtual void SetFlags(object target, DeclarationFlag state) {
        }
        internal bool? IsSubstantial(object target) {
            var flags = GetFlags(target);

            if ((flags & DeclarationFlag.IsSubstantial) != 0)
                return true;

            if ((flags & DeclarationFlag.IsVacuous) != 0)
                return false;

            return null;
        }
        internal virtual bool EnumerateForChildren {
            get { return false; }
        }

        // children
        internal virtual Value Children(DeclarationContext frame) {
            throw new InvalidOperationException();
        }
        internal virtual Value Substance(object target) {
            throw new NotSupportedException();
        }

        // leaf
        internal virtual bool IsTransparent {
            get { return false; }
        }
        internal virtual bool IsFlush {
            get { return false; }
        }
        internal virtual bool IsUnknown {
            get { return false; }
        }
        internal virtual bool IsInternalNode {
            get { return false; }
        }
        internal virtual Value GetLeaf(DeclarationContext frame, Value value) {
            return default(Value);
        }

        // substitute
        internal virtual bool UsePhysicalStack {
            get { return false; }
        }
        internal virtual bool AlwaysHasSubstance {
            get { return false; }
        }
        internal virtual bool ImplementesSubstitute {
            get { return false; }
        }
        
        internal virtual Type[] DedicatedStacks() {
            return null;
        }
        internal virtual bool HasDedicatedStack {
            get { return false; }
        }
        
        internal virtual bool IsSubstituteType(Type type) {
            return false;
        }
        internal virtual void UpdateSubstitutionTargets(Type type) {
            throw new InvalidOperationException();
        }

        internal virtual void Epilog(DeclarationContext frame, Value value) {
        }
        internal virtual Value Prolog(DeclarationContext frame, Value value) {
            return default(Value);
        }
        internal virtual Value Substitute(DeclarationContext frame, Value value) {
            return default(Value);
        }

        // debug
        internal virtual bool ImplementsToString {
            get { return false; }
        }
    }

    public struct DeclarationContext {

        public struct Enumerator : IEnumerable<DeclarationContext>, IEnumerator<DeclarationContext> {
            
            private Context.Enumerator m_enumerator;
            
            internal Enumerator(Context.Enumerator enumerator) {
                m_enumerator = enumerator;
            }

            public DeclarationContext Current {
                get { return new DeclarationContext(m_enumerator.Current); }
            }
            object IEnumerator.Current {
                get { return Current; }
            }
            public void Dispose() {
                m_enumerator.Dispose();
            }
            public bool MoveNext() {
                return m_enumerator.MoveNext();
            }
            public void Reset() {
                m_enumerator.Reset();
            }

            public Enumerator GetEnumerator() {
                return new Enumerator(m_enumerator.GetEnumerator());
            }
            IEnumerator<DeclarationContext> IEnumerable<DeclarationContext>.GetEnumerator() {
                return GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }
        public struct Enumerator<T> : IEnumerable<T>, IEnumerator<T> {
            private Enumerator m_enumerator;
            internal Enumerator(Enumerator enumerator) {
                m_enumerator = enumerator;
            }

            public T Current {
                get { return (T)m_enumerator.Current.Object; }
            }
            object IEnumerator.Current {
                get { return Current; }
            }
            public void Dispose() {
                m_enumerator.Dispose();
            }
            public bool MoveNext() {
                return m_enumerator.MoveNext();
            }
            public void Reset() {
                m_enumerator.Reset();
            }

            public Enumerator<T> GetEnumerator() {
                return new Enumerator<T>(m_enumerator.GetEnumerator());
            }
            IEnumerator<T> IEnumerable<T>.GetEnumerator() {
                return GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }
        public struct Continuation : IEnumerable<Value> {
            private Context.Continuation m_continuation;

            public Continuation(Context.Continuation continuation) {
                m_continuation = continuation;
            }

            public IEnumerator<Value> GetEnumerator() {
                return m_continuation.GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return GetEnumerator();
            }
        }

        private Context m_context;

        internal DeclarationContext(Context context) {
            m_context = context;
        }

        public bool IsEvaluating {
            get { return m_context.InfoType == typeof(EvaluateInfo); }
        }
        public bool IsFlattening {
            get { return m_context.InfoType == typeof(FlattenInfo); }
        }
        public bool IsSubstantiating {
            get { return m_context.InfoType == typeof(SubstanceInfo); }
        }

        public bool IsNull {
            get { return m_context.IsNull; }
        }
        public object Local {
            get { return m_context.Local; }
            set { m_context.Local = value; }
        }
        public object Object {
            get { return m_context.Object; }
        }

        public Enumerator Ancestors(Type type = null, bool includeSelf = false) {
            var auxiliary = default(Type);
            if (type != null && DeclarationLoader.HasDedicatedStack(type))
                auxiliary = type;

            return new Enumerator(m_context.Ancestors(type, auxiliary, includeSelf));
        }
        public Enumerator<T> Ancestors<T>() {
            return new Enumerator<T>(Ancestors(typeof(T)));
        }

        public void ContinueSubstitutionAfter(DeclarationContext frame) {
            m_context.ContinueSubstitutionAfter(frame.m_context);
        }
        public void ContinueSubstitutionAt(DeclarationContext frame) {
            m_context.ContinueSubstitutionAt(frame.m_context);
        }

        public void SuppressSubstitute() {
            m_context.SuppressSubstitute();
        }
        public IEnumerable Substitute(object value) {
            Value substitute;
            if (!m_context.Substitute(new Value(value), out substitute))
                return null;

            var enumerable = substitute.Object as IEnumerable;
            if (enumerable != null)
                return enumerable;

            return new[] { substitute.Object };
        }

        public IEnumerable<object> Flatten(object value) {
            var evaluation = Evaluation<FlattenInfo>.Evaluate(value);
            return evaluation.Select(o => o.Object);
        }
        public IEnumerable<T> Flatten<T>(object value, Func<object, T> transform) {
            foreach (var o in Flatten(value)) {
                if (o is T)
                    yield return (T)o;
                else
                    yield return transform(o);
            }
        }
        public IEnumerable<Value> Evaluate(params object[] value) {
            return Evaluation<EvaluateInfo>.Evaluate(value, m_context);
        }
        public Continuation Continue(params object[] value) {
            return new Continuation(m_context.Continue(new Value(value)));
        }

        public TextReader ToText(object value) {
            var evaluation = Continue(value);
            return new TextGenerator(evaluation.Select(o => o.Character));
        }
        public string ToString(object value) {
            var result = ToText(value).ReadToEnd();
            if (string.IsNullOrEmpty(result))
                return null;
            return result;
        }

        public IEnumerable<char> ToCharacters(object value) {
            var evaluation = Evaluate(value);
            return evaluation.Where(o => o.IsCharacter).Select(o => (char)o.Character);
        }
        public bool ContainsNewLine(object value) {
            return ToCharacters(value).Any(o => Environment.NewLine.Contains(o));
        }

        public override string ToString() {
            return m_context.ToString();
        }
    }

    internal class EvaluateInfo : DeclarationEvaluationInfo {
        internal EvaluateInfo(Type type)
            : base(type) {
        }

        public override Value Children(Context context) {
            return Info.Children(new DeclarationContext(context));
        }
        public override bool IsInternalNode(Value value) {
            return Info.IsInternalNode;
        }
        public override void DeinitializeFrame(Context context) {
            Info.DeinitializeFrame(new DeclarationContext(context));
        }
    }
    internal class FlattenInfo : DeclarationEvaluationInfo {
        internal FlattenInfo(Type type)
            : base(type) {
        }

        public override bool IsInternalNode(Value value) {
            return Info.IsTransparent; 
        }
        public override Value Children(Context context) {
            return Info.Children(new DeclarationContext(context));
        }
        public override bool Result(Context context, Value value, out Value substitute) {
            substitute = default(Value);
            return false;
        }
    }
    internal class SubstanceInfo : DeclarationEvaluationInfo {
        internal SubstanceInfo(Type type)
            : base(type) {
        }

        private void MarkSubstantial(Value value) {
            var info = DeclarationLoader.GetInfo(value.Type);
            info.SetFlags(value.Object, DeclarationFlag.IsSubstantial);
        }

        public override bool IsInternalNode(Value value) {

            // declaration is attributed as always having substance
            if (Info.AlwaysHasSubstance)
                return false;

            // instance has been marked as substantial
            var flags = Info.GetFlags(value.Object);
            if ((flags & DeclarationFlag.IsSubstantial) != 0)
                return false;

            // flush is the only vacuous leaf node
            if (Info.IsFlush)
                return true;

            return Info.IsInternalNode; 
        }
        public override Value Children(Context context) {
            var value = context.Value;
            if (value.IsCharacter)
                return value;

            // substance is computed without context 
            // so that its result may be cached
            return Info.Substance(value.Object);
        }

        public override bool Prolog(Context context, Value value, out Value substitute) {
            substitute = default(Value);
            var info = DeclarationLoader.GetInfo(value.Type);

            // skip value if previously discovered to be vacuous 
            var flags = info.GetFlags(value.Object);
            if ((flags & DeclarationFlag.IsVacuous) != 0)
                return true;

            return base.Prolog(context, value, out substitute);
        }
        public override void Epilog(Context context, Value value) {
            // all popped nodes are vacuous
            var info = DeclarationLoader.GetInfo(value.Type);
            info.SetFlags(value.Object, DeclarationFlag.IsVacuous);
        }
        public override bool Result(Context context, Value value, out Value substitute) {
            substitute = default(Value);

            // mark value as substantial
            MarkSubstantial(value);

            // mark all nodes on stack as substantial            
            foreach (var ancestor in context.Ancestors())
                MarkSubstantial(ancestor.Value); // todo

            // evluation will terminate after this result
            return false;
        }
    }

    internal abstract class DeclarationEvaluationInfo : Info {

        private DeclarationInfo m_info;

        internal DeclarationEvaluationInfo(Type type) {
            m_info = DeclarationLoader.GetInfo(type);
        }

        protected DeclarationInfo Info {
            get { return m_info; }
        }

        public override void InitializeFrame(Context context) {
            m_info.InitializeFrame(new DeclarationContext(context));
        }

        public override bool UsePhysicalStack {
            get { return Info.UsePhysicalStack; }
        }
        public override bool ImplementsSubstitute {
            get { return Info.ImplementesSubstitute; }
        }
        public override bool IsSubstituteType(Type type) {
            return Info.IsSubstituteType(type);
        }
        
        public override Type[] AuxiliaryStacks() {
            return m_info.DedicatedStacks(); 
        }

        public override bool EnumerateForChildren {
            get { return Info.EnumerateForChildren; }
        }

        private bool VanishOptimization(ref Value substitute) {
            if (substitute.IsReference && substitute.Object == Declaration.Vanish) {
                substitute = default(Value);
                return true;
            }

            return false;
        }
        public override bool Prolog(Context context, Value value, out Value substitute) {
            substitute = m_info.Prolog(new DeclarationContext(context), value);

            if (VanishOptimization(ref substitute))
                return true;

            return !substitute.IsNull;
        }
        public override bool Substitute(Context context, Value value, out Value substitute) {
            substitute = m_info.Substitute(new DeclarationContext(context), value);

            if (VanishOptimization(ref substitute))
                return true;

            return !substitute.IsNull;
        }
        public override bool Result(Context context, Value value, out Value substitute) {
            substitute = Info.GetLeaf(new DeclarationContext(context), value);

            if (VanishOptimization(ref substitute))
                return true;

            // substitute flush with null. this is the only way to return null
            if (value.IsReference && value.Object is DeclarationFlush) {
                substitute = new Value(NullResult);
                return true;
            }

            return !substitute.IsNull;
        }
    }
}
